package com.weilx959.spt.app.controller;

import com.jfoenix.controls.JFXButton;
import com.weilx959.spt.app.context.ConfigContext;
import com.weilx959.spt.app.context.PluginConfigurationContext;
import com.weilx959.spt.app.context.PluginContext;
import com.weilx959.spt.app.model.AppConfig;
import com.weilx959.spt.app.plugin.HomePluginImpl;
import com.weilx959.spt.app.utils.ResourceBundleUtil;
import com.weilx959.spt.app.utils.StylesheetUtil;
import com.weilx959.spt.core.RootPaneContext;
import com.weilx959.spt.core.StageContext;
import com.weilx959.spt.core.component.PluginListCell;
import com.weilx959.spt.core.component.VToast;
import com.weilx959.spt.core.constants.CommonConstants;
import com.weilx959.spt.core.constants.ThemeEnum;
import com.weilx959.spt.core.interfaces.SPTPlugin;
import com.weilx959.spt.core.utils.Assert;
import com.weilx959.spt.core.utils.Try;
import com.weilx959.spt.core.vo.PluginItemVO;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;

import java.util.*;

/**
 * 主视图控制器
 *
 * @author weilx
 * @since 2024-04-16
 */
@Slf4j
public class MainViewController {
    @Getter
    @FXML
    private StackPane rootPane;

    @FXML
    public HBox topBar;

    @FXML
    private ListView<PluginItemVO> pluginListView;

    @FXML
    private StackPane rightMainPane;

    @FXML
    private JFXButton maximizeButton;

    private PluginItemVO home;

    private double x;

    private double y;

    private ResourceBundle bundle;

    private final Maximize maximize = new Maximize();

    @FXML
    private void initialize() {
        bundle = ResourceBundleUtil.get();

        bindEvent();

        RootPaneContext.set(rootPane);
        rightMainPane.setViewOrder(1);
        // 添加插件
        loadPlugins();

    }

    /**
     * 处理最小化按钮点击事件
     *
     * @param actionEvent 事件对象
     */
    public void handleMinimize(ActionEvent actionEvent) {
        actionEvent.consume();
        StageContext.get().setIconified(true);
    }

    /**
     * 处理最大化按钮点击事件
     *
     * @param actionEvent 事件对象
     */
    public void handleMaximize(ActionEvent actionEvent) {
        actionEvent.consume();
        maximize.setMaximized(!maximize.isMaximized());
    }

    /**
     * 处理关闭按钮点击事件
     *
     * @param actionEvent 事件对象
     */
    public void handleClose(ActionEvent actionEvent) {
        actionEvent.consume();
        Event.fireEvent(StageContext.get(), new WindowEvent(StageContext.get(), WindowEvent.WINDOW_CLOSE_REQUEST));
    }

    /**
     * 绑定事件
     */
    private void bindEvent() {
        // topBar 增加双击事件
        topBar.setOnMouseClicked(event -> {
            event.consume();
            if (event.getClickCount() == 2) {
                Event.fireEvent(maximizeButton, new ActionEvent(this, maximizeButton));
            }
        });

        // topBar 增加鼠标按下事件
        topBar.setOnMousePressed(event -> {
            event.consume();
            if (event.isPrimaryButtonDown()) {
                x = event.getSceneX();
                y = event.getSceneY();
            }
        });

        // topBar 增加拖拽事件
        topBar.setOnMouseDragged(event -> {
            event.consume();
            if (!event.isPrimaryButtonDown()) { // 右鍵
                return;
            }
            if (maximize.isMaximized()) {
                // 缩放坐标
                Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
                x = maximize.getWidth() / visualBounds.getWidth() * x;
                y = maximize.getHeight() / visualBounds.getHeight() * y;
                // 解除最大化
                StageContext.get().setMaximized(false);
                StageContext.get().setFullScreen(false);
                maximize.maximized = false;
                maximizeButton.getStyleClass().remove("minimized-button");
                maximizeButton.getStyleClass().add("maximized-button");
                StageContext.get().setWidth(maximize.getWidth());
                StageContext.get().setHeight(maximize.getHeight());
            }
            StageContext.get().setX(event.getScreenX() - x);
            StageContext.get().setY(event.getScreenY() - y);
        });

        // 窗口显示后，请求焦点
        StageContext.get().setOnShown(event -> pluginListView.requestFocus());
    }

    /**
     * 加载插件
     */
    private void loadPlugins() {
        pluginListView.setCellFactory(param -> new PluginListCell());
        // 重启时PluginContext不为空
        if (MapUtils.isNotEmpty(PluginContext.getAll())) {
            PluginContext.clear();
        }
        ThemeEnum currentTheme = Optional.ofNullable(ConfigContext.get())
            .map(AppConfig::getGeneral)
            .map(AppConfig.General::getTheme)
            .orElse(CommonConstants.DEFAULT_THEME);
        final List<String> stylesheets = new ArrayList<>(); // 需要新增的插件样式
        ServiceLoader.load(SPTPlugin.class)
            .stream()
            .map(ServiceLoader.Provider::get)
            .sorted(Comparator.comparingInt(SPTPlugin::getOrder))
            .forEach(plugin -> Try.of(() -> this.checkPlugin(plugin))
                .failure(e -> VToast.error(e.getMessage()))
                .success(v -> {
                    PluginContext.put(plugin.getID(), plugin);
                    addPlugin(plugin);
                    Optional.of(plugin)
                        .map(p -> p.getStylesheets(currentTheme))
                        .ifPresent(stylesheets::addAll);
                })
            );
        // 添加样式文件
        Platform.runLater(() -> StylesheetUtil.addStylesheets(stylesheets));

        pluginListView.getSelectionModel().selectedItemProperty().addListener(new PluginChangeListener(pluginListView, rightMainPane));
        pluginListView.getSelectionModel().select(home);
    }

    /**
     * 检查插件
     *
     * @param plugin 插件
     */
    private void checkPlugin(SPTPlugin plugin) {
        Assert.notNull(plugin, bundle.getString("app.plugin.message.empty"));
        Assert.notNull(plugin.getController(), bundle.getString("app.plugin.message.controller.empty"));
        Assert.notNull(plugin.getView(), bundle.getString("app.plugin.message.view.empty"));
        Assert.notNull(plugin.getName(), bundle.getString("app.plugin.message.name.empty"));
        Assert.notNull(plugin.getID(), bundle.getString("app.plugin.message.id.empty"));
        Assert.isNull(PluginContext.get(plugin.getID()), plugin.getName() + bundle.getString("app.plugin.message.id.duplicate"));
    }

    private void addPlugin(SPTPlugin plugin) {
        PluginItemVO pluginItemVO = PluginItemVO.builder().id(plugin.getID()).plugin(plugin).build();
        pluginListView.getItems().add(pluginItemVO);

        if (plugin instanceof HomePluginImpl) {
            this.home = pluginItemVO;
        }
    }

    /**
     * 插件更改监听器
     *
     * @author weilx
     * @since 2024-07-19
     */
    private record PluginChangeListener(ListView<PluginItemVO> pluginView, StackPane displayPane) implements ChangeListener<PluginItemVO> {
        @Override
        public void changed(ObservableValue<? extends PluginItemVO> observable, PluginItemVO oldValue, PluginItemVO newValue) {
            boolean canClose = true;

            if (oldValue != null) {
                // 关闭旧的插件
                var plugin = oldValue.getPlugin();
                var view = plugin.getView();

                if (!displayPane.getChildren().contains(view)) {
                    // 旧的插件不在展示面板中，不关闭, 新的插件也不加载
                    canClose = false;
                } else {
                    canClose = plugin.getController().onClose();

                    if (canClose) {
                        displayPane.getChildren().remove(view);
                    } else {
                        // 关闭失败，恢复选择，这里触发的变更事件中旧插件（即当前的newValue）没有展示在面板中
                        pluginView.getSelectionModel().select(oldValue);
                    }
                }
            }
            if (newValue != null && canClose) {
                // 打开新的插件
                var plugin = newValue.getPlugin();
                Map<String, Object> configMap = Optional.ofNullable(plugin.getConfig())
                    .map(config -> config.deserialize(PluginConfigurationContext.get(newValue.getId())))
                    .orElseGet(HashMap::new);
                plugin.getController().onShow(configMap);
                displayPane.getChildren().add(plugin.getView());
            }
        }
    }

    @Data
    private class Maximize {
        private double width;
        private double height;
        private double x;
        private double y;
        private boolean maximized;

        public void setMaximized(boolean maximized) {
            this.maximized = maximized;

            if (maximized) {
                // 记录原始状态
                maximize.setX(StageContext.get().getX());
                maximize.setY(StageContext.get().getY());
                maximize.setWidth(StageContext.get().getWidth());
                maximize.setHeight(StageContext.get().getHeight());
                // 最大化
                maximizeButton.getStyleClass().remove("maximized-button");
                maximizeButton.getStyleClass().add("minimized-button");
                Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
                Stage stage = StageContext.get();
                stage.setX(visualBounds.getMinX());
                stage.setY(visualBounds.getMinY());
                stage.setWidth(visualBounds.getWidth());
                stage.setHeight(visualBounds.getHeight());
            } else {
                StageContext.get().setMaximized(false);
                StageContext.get().setFullScreen(false);
                // 复原
                maximizeButton.getStyleClass().remove("minimized-button");
                maximizeButton.getStyleClass().add("maximized-button");
                StageContext.get().setX(maximize.getX());
                StageContext.get().setY(maximize.getY());
                StageContext.get().setWidth(maximize.getWidth());
                StageContext.get().setHeight(maximize.getHeight());
            }
        }
    }
}
